#ifndef __DEVICE_IDE_H
#define __DEVICE_IDE_H
#include "stdint.h"
#include "sync.h"
#include "list.h"
#include "bitmap.h"

// 分区结构
struct partition {
    uint32_t start_lba;     // 起始扇区
    uint32_t sec_cnt;       // 扇区数
    struct disk* my_disk;   // 分区所属的硬盘
    struct list_elem part_tag; // 用于队列标记 起到标识自身结构的作用
    char name[8];           // 分区名称
    struct super_block* sb; // 本分区的超级块
    struct bitmap block_bitmap; // 块位图
    struct bitmap inode_bitmap; // inode 位图
    struct list open_inodes;    // 本分区打开的 i 结点队列
};

// 硬盘结构
struct disk {
    char name[8];                       // 本硬盘的名称，如 sda、sdb
    struct ide_channel* my_channel;     // 此块硬盘归属于哪个 IDE 通道
    uint8_t dev_no;                     // 本硬盘是主盘(0)还是从盘(1)
    struct partition prim_parts[4];     // 主分区数量，最多 4 个分区
    struct partition logic_parts[8];    // 逻辑分区数量，理想上有无限个，但万物皆有尽头
};

// ATA 通道结构
struct ide_channel {
    char name[8];   // 该 ATA 通道名称，即所插入的 IDE 插槽是哪个
    uint16_t port_base; // 本通道的起始端口号
                        // 这里我们只处理两个通道的主板，每个通道的端口范围是不一样的
                        // 通道1(Primary 通道) 
                            // Command Block Register 范围是 0x1F0~0x1F7
                            // Control Block Register 是 0x3F6
                            // 基数为 0x1F0，只需要增加 0~7 便可得到命令块寄存器，而增加 0x206 便可得到控制块寄存器
                        // 通道2(Secondary 通道) 
                            // Command Block Register 范围是 0x170~0x177
                            // Control Block Register 是 0x376
                            // 同理，基数为 0x170
    uint8_t irq_no;     // 本通道所用的中断号
                        // 在硬盘的中断处理程序中要根据中断号来判断在哪个通道中操作
                        // 0x2E 或 0x2F, 它们分别是从片 8259A 的 IRQ14 和 IRQ15 接口
    struct lock lock; // 一个通道只能有 1 个中断信号，因此通道中的两个硬盘也只能共用同一个中断。
                      // 中断发生时无法根据中断信号来区分到底是哪个硬盘
                      // 所以一次只允许对通道中的一个硬盘进行操作
                      // 因此在通道中设置锁来实现互斥
    bool expecting_intr; // 向硬盘发完命令后，该通道会等待来自硬盘的中断
                         // true: 等待; false: 不等待
    struct semaphore disk_done; // 驱动程序向硬盘发送命令后，在等待硬盘工作期间可以通过此信号量阻塞自己，避免干等着浪费 CPU。
                                // 等硬盘工作完成后会发出中断，中断处理程序通过此信号量将硬盘驱动程序唤醒。
    struct disk devices[2]; // 一个通道上连接两个硬盘，一主一从
};

void intr_hd_handler(uint8_t irq_no);
void ide_init(void);
extern uint8_t channel_cnt;
extern struct ide_channel channels[];
extern struct list partition_list;
void ide_read(struct disk* hd, uint32_t lba, void* buf, uint32_t sec_cnt);
void ide_write(struct disk* hd, uint32_t lba, void* buf, uint32_t sec_cnt);
#endif
